package org.jflame.context.spring.aop;

import org.jflame.commons.reflect.ReflectionHelper;
import org.jflame.commons.util.ArrayHelper;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.core.StandardReflectionParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

public class SpelKeyParser implements KeyExprParser {

    private static Map<String,Expression> expressionCache = new ConcurrentHashMap<>();
    private ExpressionParser parser = new SpelExpressionParser();
    private StandardReflectionParameterNameDiscoverer discoverer = new StandardReflectionParameterNameDiscoverer();

    @Override
    public boolean support(String keyExpression) {
        return keyExpression.startsWith("#{") && keyExpression.endsWith("}");
    }

    @Override
    public String parse(final String key, final Method method, final Object[] args) {
        String methodSignature = ReflectionHelper.toSignatureString(method);
        Expression expression = buildExpression(methodSignature + ":" + key, method);
        String[] paramNames = discoverer.getParameterNames(method);
        if (ArrayHelper.isNotEmpty(paramNames)) {
            EvaluationContext context = new StandardEvaluationContext();
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }
            return methodSignature + ":" + expression.getValue(context, String.class);
        } else {
            return methodSignature + ":" + expression.getValue(String.class);
        }
    }

    private Expression buildExpression(final String cacheKey, final Method m) {
        Expression exp = expressionCache.get(cacheKey);
        if (exp == null) {
            exp = parser.parseExpression(cacheKey, ParserContext.TEMPLATE_EXPRESSION);
            expressionCache.put(cacheKey, exp);
        }
        return exp;
    }

}
